package alto_nivel.gals;

import java.util.ArrayList;
import java.util.List;

import alto_nivel.compilador.NoPilhaOperandos;
import alto_nivel.compilador.PilhaOperandos;
import alto_nivel.compilador.Tipo;

public class SemanticoAltoNivel implements Constants {

    /** token reconhecido no momento da ação semântica */
    private Token token;

    /** armazena o nome do programa declarado */
    private String nomePrograma;

    /** Lista de comandos na linguagem objeto */
    private List<String> linguagemObjeto;

    /** número dos registradores (atributos) declarados */
    private int numeroRegistrador;

    /** endereco das instruções monolíticas geradas */
    private int enderecoInstrucao;

    /** pilha utilizada para armazenar os ids/constantes inteiras nas operações */
    private PilhaOperandos operandos;

    /** lista para armazenar os endereços das instruções que precisam ser atualizadas devido ao if */
    private List<Integer> enderecosIf;

    /** lista para armazenar os atributos/identificadores declarados */
    private List<ItemTabelaSimbolos> tabelaSimbolos;

    /** lista que armazena o string lexema dos atributos declarados */
    private List<String> idsAltoNivel;

    /** lista que armazena o string lexema dos atributos de retorno */
    private List<String> idsRetorno;

    /** lista que armazena o string lexema dos parametros no cabeçalho */
    private List<String> idsParametros;

    /** lista que armazena o valor inteiro dos atributos declarados */
    private List<Integer> valoresAltoNivel;

    /** Usado para armazenar o operador relacional reconhecido */
    private String operador;

    /** identifica os endereços em que são iniciados os loops de comandos */
    private List<Integer> inicioLoops;

    /** identifica os endereços em que são finalizados os loops de comandos */
    private List<Integer> finalLoops;

    private List<Boolean> listaAberturaIfs;

    private List<Boolean> listaBreaksValidos;

    // Semântica
    private Tipo[] tiposExpressao;
    private int posTipo;

    public SemanticoAltoNivel() {

	posTipo = 0;
	numeroRegistrador = 0;
	enderecoInstrucao = 1;

	operador = "";

	tabelaSimbolos = new ArrayList<ItemTabelaSimbolos>();
	idsAltoNivel = new ArrayList<String>();
	idsParametros = new ArrayList<String>();
	idsRetorno = new ArrayList<String>();
	enderecosIf = new ArrayList<Integer>();
	valoresAltoNivel = new ArrayList<Integer>();
	tiposExpressao = new Tipo[] { Tipo.NENHUM, Tipo.NENHUM };
	linguagemObjeto = new ArrayList<String>();
	operandos = new PilhaOperandos();

	inicioLoops = new ArrayList<Integer>();
	finalLoops = new ArrayList<Integer>();

	listaAberturaIfs = new ArrayList<Boolean>();
	listaBreaksValidos = new ArrayList<Boolean>();
    }

    public void executeAction(int action, Token token) throws SemanticError {
	this.token = token;

	switch (action) {

	case 1:
	    acao01();
	    break;
	case 2:
	    acao02();
	    break;
	case 3:
	    acao03();
	    break;
	case 4:
	    acao04();
	    break;
	case 5:
	    acao05();
	    break;
	case 6:
	    acao06();
	    break;
	case 7:
	    acao07();
	    break;
	case 8:
	    acao08();
	    break;
	case 9:
	    acao09();
	    break;
	case 10:
	    acao10();
	    break;
	case 11:
	    acao11();
	    break;
	case 12:
	    acao12();
	    break;
	case 13:
	    acao13();
	    break;
	case 14:
	    acao14();
	    break;
	case 15:
	    acao15();
	    break;
	case 16:
	    acao16();
	    break;
	case 17:
	    acao17();
	    break;
	case 18:
	    acao18();
	    break;
	case 19:
	    acao19();
	    break;
	case 20:
	    acao20();
	    break;
	case 21:
	    acao21();
	    break;
	case 22:
	    acao22();
	    break;
	case 23:
	    acao23();
	    break;
	case 24:
	    acao24();
	    break;
	case 25:
	    acao25();
	    break;
	}
    }

    /**
     * Ação 25: método responsável por armazenar em uma lista de controle os atributos utilizados
     * como parâmetros no programa. Semelhante a ação número 01 e 02.
     */
    private void acao25() {

	ItemTabelaSimbolos item = new ItemTabelaSimbolos();
	item.setIdentificador(token.getLexeme());
	item.setTipo(Tipo.INT);
	item.setValorSequencial(++numeroRegistrador);

	tabelaSimbolos.add(item);

	idsParametros.add(token.getLexeme());
    }

    /**
     * Ação 01: declaração de atributos. Checa duplicidade e cria os objetos para tabela de
     * símbolos.
     * 
     * @throws SemanticError
     */
    private void acao01() throws SemanticError {

	for (int i = 0; i < idsAltoNivel.size(); i++) {

	    String id = idsAltoNivel.get(i);

	    ItemTabelaSimbolos item = getSimbolo(id);
	    // se já tá declarado com valor (nao é parametro), é id duplicado
	    if (item != null && item.getValorDeclarado() != null) {
		String msg = token.getLexeme()
		    + " identificador declarado mais de uma vez";
		throw new SemanticError(msg, token.getPosition());
	    }

	    if (item != null) {
		int indiceEncontrado = -1;
		for (int z = 0; z < tabelaSimbolos.size(); z++) {
		    if (tabelaSimbolos.get(z) == item) {
			indiceEncontrado = z;
		    }
		}

		tabelaSimbolos.remove(item);
		item.setValorDeclarado(valoresAltoNivel.get(i));

		criarInstrucaoDeclaracaoAtributo(item);
		tabelaSimbolos.add(indiceEncontrado, item);

	    } else {

		item = new ItemTabelaSimbolos();
		item.setTipo(Tipo.INT);
		item.setIdentificador(id);
		item.setValorSequencial(++numeroRegistrador);
		item.setValorDeclarado(valoresAltoNivel.get(i));

		criarInstrucaoDeclaracaoAtributo(item);
		tabelaSimbolos.add(item);
	    }

	}

	idsAltoNivel.clear();
	posTipo = 0;
    }

    private void criarInstrucaoDeclaracaoAtributo(ItemTabelaSimbolos item) {

	String instrucao = enderecoInstrucao + ": faca "
	    + item.getItemMonolitico() + " = " + item.getValorDeclarado()
	    + " va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(instrucao);
    }

    /**
     * Ação 02: adiciona o nome do atributo em uma lista de controle. O controle refere-se a
     * manipulação do atributo/identificador. Ele poderá ser:
     * 
     * - incrementado; - decrementado; - atribuído o valor de uma nova expressão qualquer do mesmo
     * tipo.
     * 
     * A ação 05 tem ligação diretao a ação 02: refere-se ao fechamento da atribuição deste
     * identificador.
     */
    private void acao02() {
	idsAltoNivel.add(token.getLexeme());
    }

    /**
     * Ação 03: gera código para soma de registrador/atributo.
     */
    private void acao03() {

	String registrador = getSimbolo(idsAltoNivel.get(0))
	    .getItemMonolitico();
	criarInstrucaoInc(registrador);

	idsAltoNivel.clear();
    }

    /**
     * Ação 04: gera código para subtração de registrador/atributo.
     */
    private void acao04() {

	String registrador = getSimbolo(idsAltoNivel.get(0))
	    .getItemMonolitico();
	criarInstrucaoDec(registrador);

	idsAltoNivel.clear();
    }

    /**
     * Ação 05: atribui o resultado dos comandos armazenados em registradores ao atributo inicial
     * pretendido.
     */
    private void acao05() throws SemanticError {

	ItemTabelaSimbolos item = getSimbolo(idsAltoNivel.get(0));

	if (item == null) {
	    String msg = idsAltoNivel.get(0) + " identificador não declarado";
	    throw new SemanticError(msg, token.getPosition());
	}

	tiposExpressao[posTipo++] = item.getTipo();
	if (verifica("=") == Tipo.NENHUM) {
	    // throw new SemanticError("Tipos incompatíveis",
	    // token.getPosition());
	}

	String instrucao = enderecoInstrucao + ": faca "
	    + item.getItemMonolitico() + " = "
	    + operandos.popNoPilha().getRegistrador() + " va_para "
	    + ++enderecoInstrucao;
	linguagemObjeto.add(instrucao);

	idsAltoNivel.clear();
    }

    /**
     * Ação 06: Saída do teste: atualiza va_para no último comando do bloco 'então' (pra não entrar
     * no senão).
     */
    private void acao06() {

	removerUltimoObjetoLista(listaAberturaIfs);

	int enderecoOld = enderecosIf.get(enderecosIf.size() - 1);
	enderecosIf.remove(enderecosIf.size() - 1);

	String instrucaoOld = linguagemObjeto.get(enderecoOld - 1);

	// se tiver dentro de outro if, nao atualiza ainda
	if (enderecosIf.isEmpty()) {

	    instrucaoOld = instrucaoOld.replace("XZ", String
		.valueOf(enderecoInstrucao));
	    linguagemObjeto.remove(enderecoOld - 1);
	    linguagemObjeto.add(enderecoOld - 1, instrucaoOld);

	    // arruma va_para dos ultimos comandos que estavam dentro de outros
	    // ifs
	    for (int i = 0; i < linguagemObjeto.size(); i++) {

		if (linguagemObjeto.get(i).contains("XZ")) {

		    String s = linguagemObjeto.get(i);
		    linguagemObjeto.remove(i);
		    s = s.replace("XZ", String.valueOf(enderecoInstrucao));
		    linguagemObjeto.add(i, s);
		}
	    }
	}

    }

    /**
     * Ação 07: armazena os valores inteiros declarados para os atributos de alto nível.
     */
    private void acao07() {
	valoresAltoNivel.add(Integer.parseInt(token.getLexeme()));
    }

    /**
     * Ação 08: método que empilha o endereço da instrução if para posterior atualizaçao do
     * senao/va_para. Gera "RT = último_registrador", para poder fazer o "se T".
     */
    private void acao08() {

	listaAberturaIfs.add(true);

	String comandoRT = enderecoInstrucao
	    + ": faca RT = R" + numeroRegistrador
	    + " va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(comandoRT);

	enderecosIf.add(enderecoInstrucao);

	String comandoIf = enderecoInstrucao + ": se T entao va_para "
	    + ++enderecoInstrucao + " senao va_para XY";

	linguagemObjeto.add(comandoIf);
    }

    /**
     * Ação 09: atualiza comando if com valor do senão/va_para.
     */
    private void acao09() {

	int enderecoOld = getUltimoObjetoLista(enderecosIf);
	removerUltimoObjetoLista(enderecosIf);

	String instrucaoOld = linguagemObjeto.get(enderecoOld - 1);

	// o valor XY marcado para atualização é a instrução atual, que é o senão
	instrucaoOld = instrucaoOld.replace("XY", String.valueOf(enderecoInstrucao));
	linguagemObjeto.remove(enderecoOld - 1);
	linguagemObjeto.add(enderecoOld - 1, instrucaoOld);

	// adiciona ultimo comando dentro do entao, ele terá o va_para
	// atualizado para fora do do if (pra nao entrar no bloco senao)
	enderecosIf.add(enderecoInstrucao - 1);

	// marcando o va_para do ultimo comando do entao com XZ para atualizar no final do teste
	String ultimoComandoEntao = linguagemObjeto.get(enderecoInstrucao - 2);
	String[] split = ultimoComandoEntao.split("va_para");
	split[1] = "va_para XZ";
	ultimoComandoEntao = split[0] + split[1];

	linguagemObjeto.remove(enderecoInstrucao - 2);
	linguagemObjeto.add(enderecoInstrucao - 2, ultimoComandoEntao);

    }

    /**
     * Ação 10: armazena operador relacional.
     */
    private void acao10() {
	operador = token.getLexeme();
    }

    /**
     * Ação 11: cria operação relacional.
     * 
     * @throws SemanticError
     */
    private void acao11() throws SemanticError {

	if (verifica(operador) == Tipo.NENHUM) {
	    // throw new SemanticError("Tipos incompatíveis",
	    // token.getPosition());
	}

	NoPilhaOperandos[] vetorOperandos = prepararOperandos();

	if (operador.equals("==")) {
	    criarInstrucaoIgualdade(vetorOperandos[0], vetorOperandos[1]);

	} else if (operador.equals("<")) {
	    criarInstrucaoValorMenor(vetorOperandos[0], vetorOperandos[1]);

	} else if (operador.equals("<=")) {
	    criarInstrucaoMenorIgual(vetorOperandos[0], vetorOperandos[1]);
	}
    }

    /**
     * Ação 12: Método que cria instrução de adição de registradores/atributos/constantes.
     * 
     * @throws SemanticError
     */
    private void acao12() throws SemanticError {

	if (verifica("ADD") == Tipo.NENHUM) {
	    // throw new SemanticError("Tipos incompatíveis",
	    // token.getPosition());
	}

	NoPilhaOperandos[] vetorOperandos = prepararOperandos();
	criarInstrucaoAdicao(vetorOperandos[0], vetorOperandos[1]);
    }

    /**
     * Ação 13: finaliza a tradução do programa.
     */
    private void acao13() {
	String fim = enderecoInstrucao + ": retorna";
	linguagemObjeto.add(fim);
    }

    /**
     * Cria a chamda de instrução de igualdade entre dois operandos.
     * 
     * @param no1
     *            o primeiro operando
     * @param no2
     *            o segundo operando
     */
    private void criarInstrucaoIgualdade(NoPilhaOperandos no1,
	NoPilhaOperandos no2) {
	numeroRegistrador++;

	String adicao = enderecoInstrucao + ": faca r" + numeroRegistrador
	    + " = Comp_Dois_Num_Iguais(" + no2.getRegistrador() + ", "
	    + no1.getRegistrador() + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(adicao);

	operandos.pushNoValorSequencial(numeroRegistrador);
    }

    /**
     * Cria a chamda de instrução de valor menos entre dois operandos.
     * 
     * @param no1
     *            o primeiro operando
     * @param no2
     *            o segundo operando
     */
    private void criarInstrucaoValorMenor(NoPilhaOperandos no1, NoPilhaOperandos no2) {

	numeroRegistrador++;

	String adicao = enderecoInstrucao + ": faca R" + numeroRegistrador
	    + " = A_Menor_B(" + no2.getRegistrador() + ", "
	    + no1.getRegistrador() + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(adicao);

	operandos.pushNoValorSequencial(numeroRegistrador);

    }

    /**
     * Cria a chamda de instrução de valor menor/igual entre dois operandos.
     * 
     * @param no1
     *            o primeiro operando
     * @param no2
     *            o segundo operando
     */
    private void criarInstrucaoMenorIgual(NoPilhaOperandos no1, NoPilhaOperandos no2) {
	numeroRegistrador++;

	String adicao = enderecoInstrucao + ": faca R" + numeroRegistrador
	    + " = A_MenorIgual_B(" + no2.getRegistrador() + ", "
	    + no1.getRegistrador() + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(adicao);

	operandos.pushNoValorSequencial(numeroRegistrador);
    }

    /**
     * Método que trata os operandos atribuindo um número de registrador, caso não tenham sido
     * declarados como atributos, e gera instrução de atribuição do registrador com o valor das
     * funções correntes.
     * 
     * @return vetor com os dois operandos preparados.
     */
    private NoPilhaOperandos[] prepararOperandos() {

	NoPilhaOperandos[] vetorOperandos = new NoPilhaOperandos[2];

	NoPilhaOperandos no1 = operandos.popNoPilha();
	NoPilhaOperandos no2 = operandos.popNoPilha();

	vetorOperandos[0] = no1;
	vetorOperandos[1] = no2;

	verificarValorSequencial(no1, no2);
	criarAtribuicaoOperandos(no1, no2);

	return vetorOperandos;
    }

    /**
     * Método que verifica se os operandos precisam receber atribuição de valor sequencial para
     * serem tratados como registradores. Se forem atributos, não gera valor sequencial.
     * 
     * @param no1
     *            o primeiro operando.
     * @param no2
     *            o segundo operando.
     */
    private void verificarValorSequencial(NoPilhaOperandos no1,
	NoPilhaOperandos no2) {

	if (no1.getValorSequencial() == 0 && no1.getItem() == null) {
	    no1.setValorSequencial(++numeroRegistrador);
	}
	if (no2.getValorSequencial() == 0 && no2.getItem() == null) {
	    no2.setValorSequencial(++numeroRegistrador);
	}
    }

    /**
     * Método que verifica se os operandos precisam gerar instrução de atribuição de valor.
     * 
     * @param no1
     *            o primeiro operando.
     * @param no2
     *            o segundo operando.
     */
    private void criarAtribuicaoOperandos(NoPilhaOperandos no1,
	NoPilhaOperandos no2) {
	// se for um id ou registrador temporário, teoricamente tem valor
	// atribuido, portanto nao precisa gerar atribuição para registrador
	// temporario
	if (no1.getItem() == null && !no1.isResultadoTemporario()) {
	    criarInstrucaoAtribuicao(no1);
	}
	if (no2.getItem() == null && !no2.isResultadoTemporario()) {
	    criarInstrucaoAtribuicao(no2);
	}
    }

    /**
     * Método que cria a instrução de atribuição para um operando que não teve seu valor declarado
     * como atributo e não foi criado como valor temporário de uma operação.
     * 
     * @param no
     *            o operando que será atribuído o valor.
     */
    private void criarInstrucaoAtribuicao(NoPilhaOperandos no) {

	String instrucao = enderecoInstrucao + ": faca R"
	    + no.getValorSequencial() + " = " + no.getValor() + " va_para "
	    + ++enderecoInstrucao;

	linguagemObjeto.add(instrucao);
    }

    /**
     * Método que gera a instrução de adição entre operandos.
     * 
     * @param no1
     *            o primeiro operando.
     * @param no2
     *            o segundo operando.
     */
    private void criarInstrucaoAdicao(NoPilhaOperandos no1, NoPilhaOperandos no2) {

	numeroRegistrador++;

	String adicao = enderecoInstrucao + ": faca R" + numeroRegistrador
	    + " = Soma_Sem_Sinal(" + no1.getRegistrador() + ", "
	    + no2.getRegistrador() + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(adicao);

	operandos.pushNoValorSequencial(numeroRegistrador);
    }

    /**
     * Método que trata da geração de instrução de subtração entre operandos.
     * 
     * @throws SemanticError
     */
    private void acao14() throws SemanticError {

	if (verifica("SUB") == Tipo.NENHUM) {
	    // throw new SemanticError("Tipos incompatíveis",
	    // token.getPosition());
	}

	NoPilhaOperandos[] vetorOperandos = prepararOperandos();
	criarInstrucaoSubtracao(vetorOperandos[0], vetorOperandos[1]);

    }

    /**
     * Método auxiliar para criação de instrução de subtração entre dois operandos.
     * 
     * @param no1
     *            o primeiro operando.
     * @param no2
     *            o segundo operando.
     */
    private void criarInstrucaoSubtracao(NoPilhaOperandos no1, NoPilhaOperandos no2) {

	numeroRegistrador++;

	String subtracao = enderecoInstrucao + ": faca R" + numeroRegistrador
	    + " = Subtrai(" + no2.getRegistrador() + ", "
	    + no1.getRegistrador() + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(subtracao);

	operandos.pushNoValorSequencial(numeroRegistrador);
    }

    /**
     * Ação de multiplicação. Desempilha dois operandos e armazena-os em registrador temporário.
     * Gera chamada de função de multiplicação para os dois operandos.
     * 
     * @throws SemanticError
     *             erro semântico ocasionado por tipos incompatíveis
     */
    private void acao15() throws SemanticError {

	Tipo tipo = verifica("MUL");
	if (tipo == Tipo.NENHUM) {
	    // throw new SemanticError("Tipos incompatíveis",
	    // token.getPosition());
	}

	tiposExpressao[posTipo++] = tipo;

	NoPilhaOperandos[] vetorOperandos = prepararOperandos();
	criarInstrucaoMultiplicacao(vetorOperandos[0], vetorOperandos[1]);

    }

    /**
     * Método auxiliar para criação de instrução de multiplicação entre dois operandos.
     * 
     * @param no1
     *            o primeiro operando.
     * @param no2
     *            o segundo operando.
     */
    private void criarInstrucaoMultiplicacao(NoPilhaOperandos no1, NoPilhaOperandos no2) {

	numeroRegistrador++;

	String multiplicacao = enderecoInstrucao
	    + ": faca R" + numeroRegistrador + " = Mult_Int_Sem_Sinal("
	    + no2.getRegistrador() + ", "
	    + no1.getRegistrador()
	    + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(multiplicacao);

	operandos.pushNoValorSequencial(numeroRegistrador);
    }

    /**
     * Método que trata da geração de instruçção de divisão com representação da parte inteira entre
     * dois operandos.
     * 
     * @throws SemanticError
     */
    private void acao16() throws SemanticError {

	Tipo tipo = verifica("DIV");
	if (tipo == Tipo.NENHUM) {
	    // throw new SemanticError("Tipos incompatíveis",
	    // token.getPosition());
	}

	tiposExpressao[posTipo++] = tipo;

	NoPilhaOperandos[] vetorOperandos = prepararOperandos();
	criarInstrucaoDivisao(vetorOperandos[0], vetorOperandos[1]);
    }

    /**
     * Método auxiliar para criação de instrução de divisão entre dois operandos.
     * 
     * @param no1
     *            o primeiro operando.
     * @param no2
     *            o segundo operando.
     */
    private void criarInstrucaoDivisao(NoPilhaOperandos no1, NoPilhaOperandos no2) {

	numeroRegistrador++;

	String divisao = enderecoInstrucao + ": faca R" + numeroRegistrador
	    + " = Div(" + no2.getRegistrador() + ", "
	    + no1.getRegistrador() + ") va_para " + ++enderecoInstrucao;

	linguagemObjeto.add(divisao);

	operandos.pushNoValorSequencial(numeroRegistrador);
    }

    /**
     * Ação 17: método que verifica se o id utilizado na expressão foi declarado e adiciona-o à
     * pilha de operandos, caso estiver ok.
     * 
     * @throws SemanticError
     */
    private void acao17() throws SemanticError {

	String id = token.getLexeme();
	ItemTabelaSimbolos item = getSimbolo(id);

	if (item == null) {
	    String msg = id + " identificador não declarado";
	    throw new SemanticError(msg, token.getPosition());
	}

	tiposExpressao[posTipo++] = item.getTipo();

	operandos.pushNoItem(item);
    }

    /**
     * Ação 18: método que cria um nó de operando com base no valor inteiro utilizado na expressão
     * matemática.
     * 
     */
    private void acao18() {

	int valor = Integer.parseInt(token.getLexeme());
	operandos.push(valor);
    }

    /**
     * Ação 19: método que armazena o valor da instrução atual antes de entrar no loop de comandos.
     */
    private void acao19() {
	inicioLoops.add(new Integer(enderecoInstrucao));
    }

    /**
     * Ação 20: método que identifica a saída do loop de comandos e atualiza o va_para do último
     * comando do loop para voltar ao início.
     */
    private void acao20() {

	// gera comando pra voltar pro início do loop se usou break em IF
	if (!listaBreaksValidos.isEmpty()) {

	    String reinicio = enderecoInstrucao + ": faca RT = 0 va_para " + getUltimoObjetoLista(inicioLoops);
	    linguagemObjeto.add(reinicio);

	    // incrementa para próxima instrução pegar o valor atualizado
	    enderecoInstrucao++;
	}

	// atualiza XL (break gerado) para fora do loop
	int enderecoBreak = getUltimoObjetoLista(finalLoops);
	String comandoBreak = linguagemObjeto.get(enderecoBreak);
	String comandoBreakAtualizado = comandoBreak.replace("XL", String.valueOf(enderecoInstrucao));

	// readiciona o break atualizado
	linguagemObjeto.remove(enderecoBreak);
	linguagemObjeto.add(enderecoBreak, comandoBreakAtualizado);

	// se o break não estiver em comando de teste, não há repetição
	removerUltimoObjetoLista(finalLoops);
	removerUltimoObjetoLista(inicioLoops);
	removerUltimoObjetoLista(listaBreaksValidos);

    }

    /**
     * Ação 21: método que marca o "va_para" para posterior atualização para fora do loop pois o
     * token atual é um break
     */
    private void acao21() {

	// verifica se o break foi utilizado de forma correta dentro de IF
	if (getUltimoObjetoLista(listaAberturaIfs) != null && getUltimoObjetoLista(listaAberturaIfs) == true) {
	    listaBreaksValidos.add(true);
	}

	// monta o comando do break (sair do laço de repetição)
	String comandoZerado = enderecoInstrucao + ": faca RT = 0 va_para XL";

	// salva o endereço onde está o break que foi gerado
	finalLoops.add(new Integer(enderecoInstrucao - 1));

	linguagemObjeto.add(comandoZerado);

	enderecoInstrucao++;
    }

    /**
     * Ação 22: método que adiciona em uma lista os atributos de retorno.
     */
    private void acao22() {
	idsRetorno.add(token.getLexeme());
    }

    /**
     * Ação 23: verifica se a função de retorno possui todos os atributos declarados.
     * 
     * @throws SemanticError
     *             erro caso a função de retorno possua atributos que não foram declarados.
     * 
     */
    private void acao23() throws SemanticError {

	for (String s : idsRetorno) {

	    if (getSimbolo(s) == null) {

		String msg = "Erro na declaração da função de retorno: o atributo "
		    + s + " não está presente na declaração de atributos.";
		throw new SemanticError(msg, token.getPosition());
	    }
	}
    }

    private void acao24() {
	nomePrograma = token.getLexeme();
    }

    /**
     * Método que busca um {@link ItemTabelaSimbolos} pelo seu id (nome).
     * 
     * @param id
     *            nome do atributo.
     * @return {@link ItemTabelaSimbolos} encontrado ou null caso não encontre.
     */
    public ItemTabelaSimbolos getSimbolo(String id) {
	for (ItemTabelaSimbolos item : tabelaSimbolos) {
	    if (id.equalsIgnoreCase(item.getIdentificador())) {
		return item;
	    }
	}
	return null;
    }

    private Tipo verifica(String operacao) {

	Tipo t1 = tiposExpressao[0];
	Tipo t2 = tiposExpressao[1];
	posTipo = 0;

	if (operacao.equals("=")) {
	    if (t1 == t2) {
		return t1;
	    }
	}

	if (t1 == Tipo.INT && t2 == Tipo.INT) {

	    if (operacao.equals("ADD") || operacao.equals("SUB")
		|| operacao.equals("MUL")) {
		return Tipo.INT;
	    }
	    if (operacao.equals("DIV")) {
		// return Tipo.FLOAT;
	    }
	    if (operacao.equals("==") || operacao.equals("!=")
		|| operacao.equals("<") || operacao.equals("<=")
		|| operacao.equals(">") || operacao.equals(">=")) {
		return Tipo.BOOLEAN;
	    }
	}

	if (t1 == Tipo.BOOLEAN && t2 == Tipo.BOOLEAN) {
	    if (operacao.equals("||") || operacao.equals("&&")
		|| operacao.equals("!")) {
		return Tipo.BOOLEAN;
	    }
	}

	return Tipo.NENHUM;
    }

    /**
     * Método que gera a instrução monolítica correspondente a função de incremento de registrador.
     * 
     * @param registrador
     *            o registrador que será incrementado.
     */
    private void criarInstrucaoInc(String registrador) {

	String inc = enderecoInstrucao + ": faca inc(" + registrador
	    + ") va_para " + ++enderecoInstrucao;
	linguagemObjeto.add(inc);
    }

    /**
     * Método que gera a instrução monolítica correspondente a função de decremento de registrador.
     * 
     * @param registrador
     *            o registrador que será decrementado.
     */
    private void criarInstrucaoDec(String registrador) {

	String inc = enderecoInstrucao + ": faca dec(" + registrador
	    + ") va_para " + ++enderecoInstrucao;
	linguagemObjeto.add(inc);
    }

    private <T> T getUltimoObjetoLista(List<T> lista) {
	if (!lista.isEmpty()) {
	    T objeto = lista.get(lista.size() - 1);
	    return objeto;
	}
	return null;
    }

    private <T> void removerUltimoObjetoLista(List<T> lista) {
	if (!lista.isEmpty()) {
	    lista.remove(lista.size() - 1);
	}
    }

    public List<String> getLinguagemObjeto() {
	return linguagemObjeto;
    }

    public String getNomePrograma() {
	return nomePrograma;
    }

    public String construirHeader() {

	String atributos = "";
	if (!idsParametros.isEmpty()) {
	    atributos = "(";
	    atributos = atributos + getSimbolo(idsParametros.get(0)).getItemMonolitico();
	    for (int i = 1; i < idsParametros.size(); i++) {
		atributos = atributos + "," + getSimbolo(idsParametros.get(i)).getItemMonolitico();
	    }
	    atributos = atributos + ")";
	}

	String retornos = "";
	if (!idsRetorno.isEmpty()) {
	    retornos = retornos + getSimbolo(idsRetorno.get(0)).getItemMonolitico();
	    for (int i = 1; i < idsRetorno.size(); i++) {
		retornos = retornos + "," + getSimbolo(idsRetorno.get(i)).getItemMonolitico();
	    }
	}
	String header = "programa " + nomePrograma + atributos + " -> " + retornos;

	return header;
    }
}
